#!/usr/bin/python
# -*- coding: UTF-8 -*-

from django.db import models
from django.contrib.auth.models import User

'''
    选项

    通过选项实现对字段的约束，选项如下：

    null：如果为True，表示允许为空，默认值是False。
    blank：如果为True，则该字段允许为空白，默认值是False。
    对比：null是数据库范畴的概念，blank是表单验证证范畴的。
    db_column：字段的名称，如果未指定，则使用属性的名称。
    db_index：若值为True, 则在表中会为此字段创建索引，默认值是False。
    default：默认值。
    primary_key：若为True，则该字段会成为模型的主键字段，默认值是False，一般作为AutoField的选项使用。
    unique：如果为True, 这个字段在表中必须有唯一值，默认值是False。

    ondelelte 参数

    on_delete
    当删除关联表中的数据时，当前表与其关联的行的行为。
    models.CASCADE
    删除关联数据，与之关联也删除
    models.DO_NOTHING
    删除关联数据，引发错误IntegrityError
    models.PROTECT
    删除关联数据，引发错误ProtectedError
    models.SET_NULL
    删除关联数据，与之关联的值设置为null（前提FK字段需要设置为可空）
    models.SET_DEFAULT
    删除关联数据，与之关联的值设置为默认值（前提FK字段需要设置默认值）
    models.SET
    删除关联数据，
    a. 与之关联的值设置为指定值，设置：models.SET(值)
    b. 与之关联的值设置为可执行对象的返回值，设置：models.SET(可执行对象)
'''


class Config(models.Model):
    '''
    全局配置
    '''
    #当前配置是否激活
    active = models.BooleanField(default = False)

class Index(models.Model):
    '''
    代表一个指数/基金或者股票等可统计对象
    '''
    #该指数的代号
    number = models.CharField(unique = True, max_length = 255)
    name = models.CharField(unique = True, max_length = 255)
    description = models.CharField(null = True, max_length = 1024)
    #该指数的介绍链接
    link = models.CharField(null = True, max_length = 255)
    #该指数的状态更新脚本，ManyToOne Relation
    update_script = models.ForeignKey('IndexUpdateScript', on_delete = models.PROTECT)


class Fund(models.Model):
    '''
    代表一支基金或股票等可持有对象
    '''
    # 该基金的代号
    number = models.CharField(unique = True, max_length = 255)
    # 该基金所跟踪的指数
    index = models.ForeignKey('Index', on_delete = models.PROTECT)
    name = models.CharField(unique = True, max_length = 255)
    description = models.CharField(null = True, max_length = 1024)
    #该基金的介绍链接
    link = models.CharField(null = True, max_length = 255)
    #该基金的状态更新脚本，ManyToOne Relation
    update_script = models.ForeignKey('FundUpdateScript', on_delete = models.PROTECT)


class Manager(models.Model):
    '''
    基金经理
    '''
    fund = models.ForeignKey('Fund', on_delete = models.PROTECT)
    #上任日期
    date = models.DateField(default = "2020-06-21")
    name = models.CharField(max_length = 255)
    #性别
    GENDER_CHOICES = (
        (u'M', u'Male'),
        (u'F', u'Female'),
    )
    gender = models.CharField(max_length = 2, choices = GENDER_CHOICES, default = 'M')
    description = models.CharField(null = True, max_length = 1024)
    #基金经理介绍链接
    link = models.CharField(null = True, max_length = 255)


class IndexState(models.Model):
    '''
    代表一个Index某天的状态（统计数据），市盈率，分位点，市值，涨跌幅等
    '''
    #指数对象，ManyToOne Relation
    index = models.ForeignKey('Index', on_delete = models.CASCADE)
    date = models.DateField(default = "2020-06-21")
    date.editable = True
    #市盈率PE
    pe = models.FloatField(default = 0)
    #市盈率分位点
    pe_quantile = models.FloatField(default = 0)
    #市盈率机会值（30%分位点）
    pe_oppotunity = models.FloatField(default = 0)
    #市盈率危险值（70%分位点）
    pe_danger = models.FloatField(default = 0)
    #股息率Dividend Yield Ratio, TTM
    ttm = models.FloatField(default = 0)
    #涨跌幅
    increment = models.FloatField(default = 0)
    #30日均线
    ave_30day = models.FloatField(default = 0)
    #当日收盘市值或实时市值
    market_value = models.FloatField(default = 0)
        
    class Meta:
        unique_together = (('index', 'date'),)

    def calculate_increment(self):
        '''
        计算相对于前一交易日的涨幅
        '''
        pre = IndexState.objects.filter(index = self.index, date_lt = self.date).order_by('-date').values('market_value').first()
        print(pre)
        if pre.exists():
            self.increment = self.market_value - pre
            self.save()
        

    def calculate_ave_30day(self):
        '''
        计算收盘市值30日均线值
        '''
        pre30 = IndexState.objects.filter(index = self.index, date_lte = self.date).order_by('-date').values('market_value')[0:30]
        if pre30.exists():
            self.ave_30day = sum(pre30) / len(pre30)
            self.save()


class FundState(models.Model):
    '''
    代表一个Fund某天的状态，市盈率，分位点，市值，实时涨跌幅等
    '''
    #基金对象，ManyToOne Relation
    fund = models.ForeignKey('Fund', on_delete = models.CASCADE)
    date = models.DateField(default = "2020-06-21")
    date.editable = True
    # 跟踪误差 %
    track_error = models.FloatField(default = 0)
    #涨跌幅
    increment = models.FloatField(default = 0)
    #30日均线
    ave_30day = models.FloatField(default = 0)
    #市值
    market_value = models.FloatField(default = 0)
        
    class Meta:
        unique_together = (('fund', 'date'),)


class Strategy(models.Model):
    '''
    代表一个策略
    '''
    # 用户，ManyToOne Relation
    user = models.ForeignKey(User, on_delete = models.CASCADE)
    # 基金对象，ManyToOne Relation
    fund = models.ForeignKey('Fund', on_delete = models.CASCADE)
    #当前策略是否激活
    active = models.BooleanField(default = True)
    '''
    策略代码
    输入：基金状态state, 大盘状态global_states, 当前持有stock, 固定买入金额fixed_in, 浮动买入金额float_in
    输出：买入数量in(整数类型，为负表示卖出)
    '''
    script = models.TextField(null = True)
    #策略描述
    description = models.CharField(null = True, max_length = 1024)
    
    class Meta:
        unique_together = (('user', 'fund'),)


class Stock(models.Model):
    '''
    代表当前的持有量
    '''
    # 用户，ManyToOne Relation
    user = models.ForeignKey(User, on_delete = models.CASCADE)
    # 基金对象
    fund = models.OneToOneField('Fund', on_delete = models.CASCADE)
    date = models.DateField(auto_now_add = True)
    #当前持有是否激活
    active = models.BooleanField(default = True)
    #当前持有成本
    unit_price = models.FloatField(default = 0)
    #当前持有量
    holdings = models.IntegerField(default = 0)
    #当前盈亏
    profit = models.FloatField(default = 0)

    class Meta:
        unique_together = (('user', 'fund', 'date'),)


class Transaction(models.Model):
    '''
    代表一次交易
    '''
    # 用户，ManyToOne Relation
    user = models.ForeignKey(User, on_delete = models.CASCADE)
    #基金对象
    fund = models.ForeignKey('Fund', on_delete = models.CASCADE)
    date = models.DateField(auto_now_add = True)
    #正代表买入，负值代表卖出
    amount = models.IntegerField(default = 0)
        
    class Meta:
        unique_together = (('user', 'fund', 'date'),)


class IndexUpdateScript(models.Model):
    '''
    更新指数状态的脚本，多个指数可能使用同一个更新脚本，那么这个脚本只会执行一次
    输入：待更新的指数状态states，时间点
    输出：更新后的指数状态
    '''
    script = models.TextField(null = True)
    #描述
    description = models.CharField(null = True, max_length = 1024)


class FundUpdateScript(models.Model):
    '''
    更新基金状态的脚本，多个基金可能使用同一个更新脚本，那么这个脚本只会执行一次
    输入：待更新的基金状态states，时间点
    输出：更新后的基金状态
    '''
    script = models.TextField(null = True)
    #描述
    description = models.CharField(null = True, max_length = 1024)